home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 4.iso / src / apps / xconf / ConfDoc.c++ next >
C/C++ Source or Header  |  1994-08-01  |  27KB  |  958 lines

  1. /*
  2.  * Copyright 1993, 1994, Silicon Graphics, Inc.
  3.  * All Rights Reserved.
  4.  *
  5.  * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.;
  6.  * the contents of this file may not be disclosed to third parties, copied or
  7.  * duplicated in any form, in whole or in part, without the prior written
  8.  * permission of Silicon Graphics, Inc.
  9.  *
  10.  * RESTRICTED RIGHTS LEGEND:
  11.  * Use, duplication or disclosure by the Government is subject to restrictions
  12.  * as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data
  13.  * and Computer Software clause at DFARS 252.227-7013, and/or in similar or
  14.  * successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished -
  15.  * rights reserved under the Copyright Laws of the United States.
  16.  */
  17.  
  18. // Conf includefile
  19. #include "ConfDoc.h"
  20.  
  21. // unix includes
  22. #include <stdlib.h>
  23. #include <sys/socket.h>
  24. #include <malloc.h>
  25. #include <ctype.h>
  26. #include <fcntl.h>
  27. #include <unistd.h>
  28. #include <bstring.h>
  29. #include <CC/osfcn.h>
  30. #include <netdb.h>
  31. #include <arpa/inet.h>
  32.  
  33. // X11 and motif includes
  34. #include <X11/StringDefs.h>
  35. #include <Xm/Text.h>
  36. #include <Xm/PushB.h>
  37. #include <Xm/Separator.h>
  38. #include <Xm/Form.h>
  39. #include <Xm/Frame.h>
  40. #include <Xm/Label.h>
  41.  
  42. ///////////////////////////////////////////////////////////////////////////////
  43. //    callback definitions
  44. ///////////////////////////////////////////////////////////////////////////////
  45.  
  46. void ConfDoc::quitCB(Widget, XtPointer clientData, XtPointer)
  47. {
  48.    ConfDoc *obj = (ConfDoc *)clientData;
  49.  
  50.    obj->sendMsg(LOGOUT, obj->_user, obj->_group, " ");
  51.    close(obj->_socket);
  52.    exit(1);
  53. }
  54.  
  55.  
  56. void ConfDoc::usersCB(Widget, XtPointer clientData, XtPointer)
  57. {
  58.    ConfDoc *obj = (ConfDoc *)clientData;
  59.  
  60.    obj->_userList.open();
  61. }
  62.  
  63.  
  64. void ConfDoc::groupsCB(Widget, XtPointer clientData, XtPointer)
  65. {
  66.    ConfDoc *obj = (ConfDoc *)clientData;
  67.  
  68.    obj->_groupList.open();
  69. }
  70.  
  71.  
  72. void ConfDoc::helpCB(Widget, XtPointer clientData, XtPointer)
  73. {
  74.    ConfDoc *obj = (ConfDoc *)clientData;
  75.  
  76.    obj->_helpList.open();
  77. }
  78.  
  79.  
  80. void ConfDoc::recvCB(XtPointer data, int /*sock*/, XtInputId* /*id*/)
  81. {
  82.    ConfDoc *obj = (ConfDoc *)data;
  83.    struct Message msg, reply;
  84.    struct sockaddr_in f;
  85.    int fromlen = sizeof(f);
  86.    char buf[MAX_BUF_SIZE];
  87.    char tbuf[16];
  88.    time_t rawtime;
  89.  
  90.    if(recvfrom(obj->_socket, &msg, sizeof(msg),0, &f, &fromlen) < 0) {
  91.       if(errno == EBADF)
  92.          exit(-1);
  93.       perror("recvfrom");
  94.       exit(-1);
  95.    }
  96.  
  97.    // if it's an unknown group, add it to the list
  98.    if(!obj->_groupList.find(msg.group)) {
  99.       obj->_groupList.add(msg.group);
  100.       bzero(msg.data, sizeof(msg.data));  // clear send buffer
  101.       return;
  102.    }
  103.  
  104.    // if it's a broadcast message, print it and return
  105.    if(msg.flags == BROADCAST) {
  106.       time(&rawtime);
  107.       cftime(tbuf, "%I:%M ", &rawtime);
  108.       strcpy(buf, "BCAST> ");
  109.       strcat(buf, tbuf);
  110.       strcat(buf, msg.data);
  111.       obj->_recvConfText->putStr(buf);
  112.       bzero(msg.data, sizeof(msg.data));  // clear send buffer
  113.       return;
  114.    }
  115.  
  116.    // if it's for our group
  117.    if(obj->_groupList.findSelected(msg.group)) {
  118.       // add any unknown users
  119.       if(!obj->_userList.find(msg.user)) {
  120.          obj->_userList.add(msg.user);
  121.       }
  122.       // check the msg type, and do the right thing
  123.       switch(msg.flags) {
  124.          case MESSAGE :
  125.             time(&rawtime);
  126.             cftime(tbuf, "%I:%M ", &rawtime);
  127.             strcpy(buf, tbuf);
  128.             strcat(buf, msg.data);
  129.             obj->_recvConfText->putStr(buf);
  130.             break;
  131.          case USER :
  132.             // announce the name change
  133.             strcpy(buf, "NAME> '");
  134.             strcat(buf, msg.data);
  135.             strcat(buf, "' changed to '");
  136.             strcat(buf, msg.user);
  137.             strcat(buf, "'.");
  138.             obj->_recvConfText->putStr(buf);
  139.             // remove the old name from the user list
  140.             obj->_userList.remove(msg.data);
  141.             // if it's you, change yer name
  142.             if(!strcmp(msg.data, obj->_user))
  143.                strcpy(obj->_user, msg.user);
  144.             break;
  145.          case PRIVATE :
  146.             if(!strcmp(msg.user, obj->_user)) {
  147.                strcpy(buf, "PRIVATE> ");
  148.                strcat(buf, msg.data);
  149.                obj->_recvConfText->putStr(buf);
  150.             }
  151.             break;
  152.          case LOGIN :
  153.             // announce the new user
  154.             strcpy(buf, "LOGIN> ");
  155.             strcat(buf, msg.user);
  156.             obj->_recvConfText->putStr(buf);
  157.             // send a reply
  158.             f.sin_port = obj->_port;
  159.             reply.flags = REPLY;
  160.             strcpy(reply.user, obj->_user);
  161.             strcpy(reply.group, obj->_group);
  162.             sendto(obj->_socket, &reply, sizeof(reply), 0, &f, sizeof(f));
  163.             break;
  164.          case FIND :
  165.             // send a reply
  166.             f.sin_port = obj->_port;
  167.             reply.flags = REPLY;
  168.             strcpy(reply.user, obj->_user);
  169.             strcpy(reply.group, obj->_group);
  170.             sendto(obj->_socket, &reply, sizeof(reply), 0, &f, sizeof(f));
  171.             break;
  172.          case LOGOUT :
  173.             // announce the user's exit
  174.             strcpy(buf, "LOGOUT> ");
  175.             strcat(buf, msg.user);
  176.             obj->_recvConfText->putStr(buf);
  177.             // remove user from the user list
  178.             obj->_userList.remove(msg.user);
  179.             break;
  180.          case RING :
  181.             if(!strcmp(msg.user, obj->_user)) {
  182.                XBell(XtDisplay(obj->_recvText), 50);
  183.                strcpy(buf, "RING> ");
  184.                strcat(buf, msg.data);
  185.                obj->_recvConfText->putStr(buf);
  186.             }
  187.             break;
  188.          case URGENT :
  189.             strcpy(buf, "URGENT> ");
  190.             strcat(buf, msg.data);
  191.             obj->_recvConfText->putStr(buf, TRUE);
  192.             break;
  193.          case REPLY :
  194.             break;
  195.          default :
  196.             break;
  197.       }
  198.    }
  199.  
  200.    // clear the msg buffer
  201.    bzero(msg.data, sizeof(msg.data));  // clear send buffer
  202. }
  203.  
  204.  
  205. void ConfDoc::sendCB(Widget, XtPointer clientData, XtPointer callData)
  206. {
  207.    char buf[MAX_BUF_SIZE];
  208.    ConfDoc *obj = (ConfDoc *)clientData;
  209.    XmAnyCallbackStruct *data = (XmAnyCallbackStruct *)callData;
  210.  
  211.    char *tmp = XmTextGetString(obj->_sendText);
  212.  
  213.    if(strlen(tmp) > MAX_BUF_SIZE) {
  214.       obj->_recvConfText->putStr("ERROR> input too long (255 chars max).");
  215.       XtFree(tmp);
  216.       return;
  217.    }
  218.  
  219.    strcpy(buf, tmp);
  220.    XtFree(tmp);
  221.  
  222.    if(buf[0] == ':') {
  223.       switch (tolower(buf[1])) {
  224.          case 'b' :
  225.             obj->sendBroadcast(buf);
  226.             break;
  227.          case 'c' :
  228.             // obj->setupChannel(buf);
  229.             obj->_recvConfText->putStr("INFO> 'channel' not implemented.");
  230.             break;
  231.          case 'f' :
  232.             obj->_recvConfText->putStr("INFO> performing find.");
  233.             obj->findUser(buf);
  234.             break;
  235.          case 'g' :
  236.             obj->setGroup(buf);
  237.             break;
  238.          case 'h' :
  239.          case '?' :
  240.             obj->_helpList.open();
  241.             break;
  242.          case 'n' :
  243.             obj->setName(buf);
  244.             break;
  245.          case 'q' :
  246.             // make this think we're x doing a callback
  247.             obj->quitCB(NULL, (XtPointer)obj, NULL);
  248.          case 'p' :
  249.          case 's' :
  250.             obj->sendPrivate(buf, 0);
  251.             break;
  252.          case 'r' :
  253.             obj->sendPrivate(buf, 1);
  254.             break;
  255.          case 'u' :
  256.             obj->sendUrgent(buf);
  257.             break;
  258.          case 'v' :
  259.             obj->_recvConfText->putStr("INFO> xconf version 2.08");
  260.             break;
  261.          case 'w' :
  262.             obj->_userList.open();
  263.             break;
  264.          default :
  265.             obj->_recvConfText->putStr("ERROR> No such command.");
  266.             break;
  267.       }
  268.    }
  269.    else {
  270.       obj->sendMsg(MESSAGE, obj->_user, obj->_group, buf);
  271.    }
  272.    bzero(buf, MAX_BUF_SIZE);
  273.   
  274.    // reset for the next message
  275.    XmTextSetString(obj->_sendText, "");
  276.    XmTextSetInsertionPosition(obj->_sendText, 0);
  277. }
  278.  
  279. ///////////////////////////////////////////////////////////////////////////////
  280. //    end of callback definitions
  281. ///////////////////////////////////////////////////////////////////////////////
  282.  
  283. ConfDoc::ConfDoc(XtAppContext app, const char*)
  284. {
  285.    _app = app;
  286. }
  287.  
  288.  
  289. ConfDoc::~ConfDoc() {}
  290.  
  291.  
  292. void ConfDoc::init(int port, char* server)
  293. {  
  294.    _port = port;
  295.    _server = server;
  296.  
  297.    _user = (char *)malloc(30*sizeof(char));  // reserve 30 bytes for 'user'
  298.    _group = (char *)malloc(30*sizeof(char));  // reserve 30 bytes for 'group'
  299.  
  300.    // JR - working on the .xconfrc stuff..
  301.    // open the .confrc file
  302.    // _rcfile.open();
  303.    // _rcfile.read();
  304.  
  305.    if(!(_user = getenv("CONFUSER"))) {
  306.       if(!(_user = getenv("LOGNAME"))) {
  307.          _user = "def_user";
  308.       }
  309.    }
  310.    if(!(_group = getenv("CONFGROUP"))) {
  311.       _group = "def_group";
  312.    }
  313.    _userList.add(_user);
  314.    _groupList.add(_group, TRUE);
  315.    setInputLabel(_group);
  316.  
  317.    // load the help information
  318.    _helpList.add("Commands start with a colon (:) Available commands are:");
  319.    _helpList.add("( [] denotes optional characters/fields)");
  320.    _helpList.add("  :b[roadcast] msg ---- broadcast a message to all groups.");
  321.    _helpList.add("  :c[hannel] user ----- open a private channel to 'user'.");
  322.    _helpList.add("  :f[ind] user -------- report whether 'user' is logged in.");
  323.    _helpList.add("  :g[roup] group ------ change your group to 'group'.");
  324.    _helpList.add("  :h[elp] ------------- print this help message.");
  325.    _helpList.add("  :n[ame] name -------- change your name to 'name'.");
  326.    _helpList.add("  :q[uit] ------------- quit this program.");
  327.    _helpList.add("  :r[ing] user [msg] -- ring the bell of 'user'");
  328.    _helpList.add("  :s[end] user msg ---- send a private message to 'user'.");
  329.    _helpList.add("  :u[rgent] msg ------- send an urgent message.");
  330.    _helpList.add("  :v[ersion] ---------- show xconf version number.");
  331.    _helpList.add("  :w[ho] -------------- bring up the user dialog.");
  332.    _helpList.add(" ");
  333.  
  334.    if(!setupSockets()) {
  335.       exit(-1);
  336.    }
  337.  
  338.    // announce that you're here
  339.    if(!sendMsg(LOGIN, _user, _group, " "))
  340.       _recvConfText->putStr("ERROR> sendMsg() failed.");
  341. }
  342.  
  343.  
  344. Boolean ConfDoc::setupSockets()
  345. {
  346.    _addrSize = sizeof(struct sockaddr_in);
  347.  
  348.    bzero(&_sendAddr, _addrSize);
  349.    _sendAddr.sin_family = AF_INET;
  350.    _sendAddr.sin_port = htons(_port);
  351.    _sendAddr.sin_addr.s_addr = htonl(INADDR_ANY);
  352.  
  353.    bzero(&_recvAddr, _addrSize);
  354.    _recvAddr.sin_family = AF_INET;
  355.    _recvAddr.sin_port = htons(_port);
  356.    _recvAddr.sin_addr.s_addr = htonl(INADDR_ANY);
  357.  
  358.    if(_server) {
  359.       if(!setupTCP()) {
  360.          fprintf(stderr, "Error setting up connection to %s.\n", _server);
  361.          return(FALSE);
  362.       }
  363.    }
  364.    else {
  365.       if(!setupUDP()) {
  366.          fprintf(stderr, "Error setting up multicast socket.\n");
  367.          return(FALSE);
  368.       }
  369.    }
  370.  
  371.    // add the receiving socket as an input source
  372.    XtAppAddInput(_app, _socket, (XtPointer)XtInputReadMask,
  373.                 (XtInputCallbackProc)&ConfDoc::recvCB, (XtPointer)this);
  374.  
  375.    return(TRUE);
  376. }
  377.  
  378.  
  379. Boolean ConfDoc::setupTCP()
  380. {
  381.    // bind to a remote machine
  382.    _socket = socket(AF_INET, SOCK_STREAM, 0);
  383.    if(_socket < 0) {
  384.       perror("socket");
  385.       return(FALSE);
  386.    }
  387.  
  388.    struct hostent *hp = gethostbyname(_server);
  389.    if (hp == NULL) {
  390.       perror("gethostbyname");
  391.       return(FALSE);
  392.    }
  393.    memcpy(&_sendAddr.sin_addr, hp->h_addr, hp->h_length);
  394.  
  395.    if(connect(_socket, &_sendAddr, _addrSize) < 0) {
  396.       perror("connect");
  397.       return(FALSE);
  398.    }
  399.  
  400.    return(TRUE);
  401. }
  402.  
  403.  
  404. Boolean ConfDoc::setupUDP()
  405. {
  406.    _socket = socket(AF_INET, SOCK_DGRAM, 0);
  407.    if(_socket < 0) {
  408.       perror("socket");
  409.       return(FALSE);
  410.    }
  411.  
  412.    int on = 1;
  413.    // doing this before the bind allows multiple bindings to this port
  414.    if(setsockopt(_socket, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)) < 0) {
  415.       perror("setsockopt(SO_REUSEPORT)");
  416.       return(FALSE);
  417.    }
  418.  
  419.    // bind to a multicast address
  420.    struct in_addr grpAddr;
  421.    grpAddr.s_addr = inet_addr(XCONF_MCAST_GROUP);
  422.    if(!IN_MULTICAST(grpAddr.s_addr)) {
  423.       fprintf(stderr, "Invalid multicast group address.\n");
  424.       return(FALSE);
  425.    }
  426.  
  427.    u_char ttl = MAX_MCAST_HOPS;
  428.    int sz = sizeof(ttl);
  429.    if(setsockopt(_socket, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sz) < 0) {
  430.       perror("setsockopt(IP_MULTICAST_TTL)");
  431.       return(FALSE);
  432.    }
  433.    _sendAddr.sin_addr = grpAddr;
  434.  
  435.    struct ip_mreq mreq;
  436.    mreq.imr_multiaddr = grpAddr;
  437.    struct in_addr ifAddr;
  438.    ifAddr.s_addr = htonl(INADDR_ANY);
  439.    mreq.imr_interface = ifAddr;
  440.    sz = sizeof(mreq);
  441.    if(setsockopt(_socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sz) < 0) {
  442.       perror("setsockopt(IP_ADD_MEMBERSHIP)");
  443.       return(FALSE);
  444.    }
  445.  
  446.    if(bind(_socket, &_recvAddr, _addrSize) < 0) {
  447.       perror("bind");
  448.       return(FALSE);
  449.    }
  450.  
  451.    if(fcntl(_socket, F_SETFL, FASYNC) < 0) {
  452.       perror("fcntl");
  453.       return(FALSE);
  454.    }
  455.    if(fcntl(_socket, F_SETOWN, getpid()) < 0) {
  456.       perror("fcntl");
  457.       return(FALSE);
  458.    }
  459.  
  460.    return(TRUE);
  461. }
  462.  
  463.  
  464. Widget ConfDoc::build(Widget parent)
  465. {
  466.    Arg args[10];  // make sure this is large enough
  467.  
  468.    // create the main form that all the subcomponents will go in
  469.    Widget form = XmCreateForm(parent, "form", NULL, 0);
  470.    XtManageChild(form);
  471.  
  472.    // create the output label
  473.    int n = 0;
  474.    XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM);  n++;
  475.    XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM);  n++;
  476.    Widget outputLabel = XmCreateLabel(form, "outputLabel", args, n);
  477.    XtManageChild(outputLabel);
  478.  
  479.    // create a help button
  480.    n = 0;
  481.    XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM);  n++;
  482.    XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM);  n++;
  483.    Widget helpButton = XmCreatePushButton(form, "helpButton", args, n);
  484.    XtManageChild(helpButton);
  485.    XtAddCallback(helpButton, XmNactivateCallback,
  486.             (XtCallbackProc)&ConfDoc::helpCB, (XtPointer)this);
  487.  
  488.    // create a groups button
  489.    n = 0;
  490.    XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM);  n++;
  491.    XtSetArg(args[n], XmNrightAttachment, XmATTACH_WIDGET);  n++;
  492.    XtSetArg(args[n], XmNrightWidget, helpButton);  n++;
  493.    Widget usersButton = XmCreatePushButton(form, "usersButton", args, n);
  494.    XtManageChild(usersButton);
  495.    XtAddCallback(usersButton, XmNactivateCallback,
  496.             (XtCallbackProc)&ConfDoc::usersCB, (XtPointer)this);
  497.  
  498.    // create a users button
  499.    n = 0;
  500.    XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM);  n++;
  501.    XtSetArg(args[n], XmNrightAttachment, XmATTACH_WIDGET);  n++;
  502.    XtSetArg(args[n], XmNrightWidget, usersButton);  n++;
  503.    Widget groupsButton = XmCreatePushButton(form, "groupsButton", args, n);
  504.    XtManageChild(groupsButton);
  505.    XtAddCallback(groupsButton, XmNactivateCallback,
  506.             (XtCallbackProc)&ConfDoc::groupsCB, (XtPointer)this);
  507.  
  508.    // create a quit button
  509.    n = 0;
  510.    XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM);  n++;
  511.    XtSetArg(args[n], XmNrightAttachment, XmATTACH_WIDGET);  n++;
  512.    XtSetArg(args[n], XmNrightWidget, groupsButton);  n++;
  513.    Widget quitButton = XmCreatePushButton(form, "quitButton", args, n);
  514.    XtManageChild(quitButton);
  515.    XtAddCallback(quitButton, XmNactivateCallback,
  516.             (XtCallbackProc)&ConfDoc::quitCB, (XtPointer)this);
  517.  
  518.    // create a separator between the input and command areas
  519.    n = 0;
  520.    XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM);  n++;
  521.    XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM);  n++;
  522.    XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET);  n++;
  523.    XtSetArg(args[n], XmNbottomWidget, quitButton);  n++;
  524.    Widget separator = XmCreateSeparator(form, "separator", args, n);
  525.    XtManageChild(separator);
  526.  
  527.    // create a frame for the text input area
  528.    n = 0;
  529.    XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM);  n++;
  530.    XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM);  n++;
  531.    XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET);  n++;
  532.    XtSetArg(args[n], XmNbottomWidget, separator);  n++;
  533.    Widget sendFrame = XmCreateFrame(form, "sendFrame", args, n);
  534.    XtManageChild(sendFrame);
  535.  
  536.    // create a text input area
  537.    n = 0;
  538.    XtSetArg(args[n], XmNorientation, XmHORIZONTAL);  n++;
  539.    Widget sendForm = XmCreateForm(sendFrame, "sendForm", args, n);
  540.    XtManageChild(sendForm);
  541.  
  542.    // create the text input prompt
  543.    n = 0;
  544.    XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM);  n++;
  545.    XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM);  n++;
  546.    XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM);  n++;
  547.    Widget sendPrompt = XmCreateText(sendForm, "sendPrompt", args, n);
  548.    XtManageChild(sendPrompt);
  549.    XmTextSetString(sendPrompt, "msg>");
  550.  
  551.    // create the text input field
  552.    n = 0;
  553.    XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM);  n++;
  554.    XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM);  n++;
  555.    XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM);  n++;
  556.    XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET);  n++;
  557.    XtSetArg(args[n], XmNleftWidget, sendPrompt);  n++;
  558.    _sendText = XmCreateText(sendForm, "sendText", args, n);
  559.    XtManageChild(_sendText);
  560.    XtAddCallback(_sendText, XmNactivateCallback,
  561.             (XtCallbackProc)&ConfDoc::sendCB, (XtPointer)this);
  562.  
  563.    // create the input label
  564.    n = 0;
  565.    XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET);  n++;
  566.    XtSetArg(args[n], XmNbottomWidget, sendFrame);  n++;
  567.    XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM);  n++;
  568.    _inputLabel = XmCreateLabel(form, "inputLabel", args, n);
  569.    XtManageChild(_inputLabel);
  570.  
  571.    // create the text output field
  572.    n = 0;
  573.    XtSetArg(args[n], XmNeditMode, XmMULTI_LINE_EDIT);  n++;
  574.    XtSetArg(args[n], XmNeditable, False);  n++;
  575.    XtSetArg(args[n], XmNcursorPositionVisible, False);  n++;
  576.    XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET);  n++;
  577.    XtSetArg(args[n], XmNtopWidget, outputLabel);  n++;
  578.    XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM);  n++;
  579.    XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM);  n++;
  580.    XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET);  n++;
  581.    XtSetArg(args[n], XmNbottomWidget, _inputLabel);  n++;
  582.    _recvText = XmCreateScrolledText(form, "recvText", args, n);
  583.    XtManageChild(_recvText);
  584.    _recvConfText = new ConfText(_recvText, NULL);
  585.  
  586.    // send all input from the keyboard to the text input field
  587.    XtSetKeyboardFocus(form, _sendText);
  588.  
  589.    // initialize the group, user and help dialogs
  590.    _groupList.init(parent, "groups");
  591.    _userList.init(parent, "users");
  592.    _helpList.init(parent, "help");
  593.  
  594.    return(form);
  595. }
  596.  
  597.  
  598. Boolean ConfDoc::setupChannel(char *buf)
  599.    // make sure a name was supplied
  600.    if(numWords(buf) == 1) {
  601.       _recvConfText->putStr("ERROR> No name supplied.");
  602.       return(FALSE);
  603.    }
  604.  
  605.    // make sure the name's not too long
  606.    char user[30];
  607.    char *tmp = (char *)getWord(buf, 2);
  608.    if(strlen(tmp) > 30) {
  609.       _recvConfText->putStr("ERROR> Maximum name length is 30 characters.");
  610.       return(FALSE);
  611.    }
  612.    strcpy(user, tmp);
  613.  
  614.    // check if user is logged in
  615.    if(!_userList.find(user)) {
  616.       char buf[MAX_BUF_SIZE];
  617.       strcpy(buf, "ERROR> user '");
  618.       strcat(buf, user);
  619.       strcat(buf, "' not logged in.");
  620.       _recvConfText->putStr(buf);
  621.       return(FALSE);
  622.    }
  623.  
  624.    PrivateList* plist = new PrivateList();
  625.    plist->init(_sendText, "private", user, this);
  626.    plist->open();
  627. }
  628.  
  629.  
  630. Boolean ConfDoc::findUser(char *buf)
  631. {
  632.    if(numWords(buf) == 1) {
  633.       _recvConfText->putStr("ERROR> No user name supplied.");
  634.       return(FALSE);
  635.    }
  636.  
  637.    char fuser[30];
  638.    char* tmp = (char*)getWord(buf, 2);
  639.    if(strlen(tmp) > 30) {
  640.       _recvConfText->putStr("ERROR> Invalid user name.");
  641.       return(FALSE);
  642.    }
  643.    strcpy(fuser, tmp);
  644.  
  645.    sendMsg(FIND, _user, _group, fuser);
  646.    sleep(1);
  647.  
  648.    char tbuf[MAX_BUF_SIZE];
  649.    strcpy(tbuf, "INFO> User '");
  650.    strcat(tbuf, fuser);
  651.    if(!_userList.find(fuser)) {
  652.       strcat(tbuf, "' not logged in.");
  653.       _recvConfText->putStr(tbuf);
  654.    }
  655.    else {
  656.       strcat(tbuf, "' logged in.");
  657.       _recvConfText->putStr(tbuf);
  658.    }
  659.    return(TRUE);
  660. }
  661.  
  662.  
  663. Boolean ConfDoc::setGroup(char *buf)
  664. {
  665.    if(numWords(buf) == 1) {
  666.       _groupList.open();
  667.       return(TRUE);
  668.    }
  669.  
  670.    if(numWords(buf) > 1) {
  671.       char new_group[30];
  672.       char* tmp = (char*)getWord(buf, 2);
  673.       if(strlen(tmp) > 30) {
  674.          _recvConfText->putStr("ERROR> Maximum name length is 30 characters.");
  675.          return(FALSE);
  676.       }
  677.       strcpy(new_group, tmp);
  678.       if(numWords(buf) == 2) {
  679.          // add this group to the list.
  680.          sendMsg(LOGOUT, _user, _group, " ");
  681.  
  682.          strcpy(_group, new_group);
  683.  
  684.          // if it's a new group, add it to the list.
  685.          if(!_groupList.find(new_group)) {
  686.             _groupList.add(new_group, TRUE);
  687.          } else {
  688.             _groupList.select(new_group);
  689.          }
  690.          _userList.clear();
  691.          setInputLabel(new_group);
  692.          sendMsg(LOGIN, _user, _group, " ");
  693.          return(TRUE);
  694.       }
  695.       // send a message to the given group.
  696.       char data[256];
  697.       char *word;
  698.       strcpy(data, "");  // initialize local buffer
  699.       int n = 3;
  700.       while((word = getWord(buf, n++)) != NULL) {
  701.          strcat(data, word);
  702.          strcat(data, " ");
  703.       }
  704.       sendMsg(MESSAGE, _user, new_group, data);
  705.    }
  706.    return(TRUE);
  707. }
  708.  
  709.  
  710. Boolean ConfDoc::setName(char *buf)
  711. {
  712.    if(numWords(buf) == 1) {
  713.       _recvConfText->putStr("ERROR> No name supplied.");
  714.       return(FALSE);
  715.    }
  716.  
  717.    char *tmp;
  718.    char new_user[30];
  719.    char old_user[30];
  720.  
  721.    tmp = (char *)getWord(buf, 2);
  722.    if(strlen(tmp) > 30) {
  723.       _recvConfText->putStr("ERROR> Maximum name length is 30 characters.");
  724.       return(FALSE);
  725.    }
  726.    strcpy(new_user, tmp);
  727.  
  728.    // check if already used
  729.    if(_userList.find(new_user) > 0) {
  730.       char buf[MAX_BUF_SIZE];
  731.       strcpy(buf, "ERROR> user '");
  732.       strcat(buf, new_user);
  733.       strcat(buf, "' already logged in.");
  734.       _recvConfText->putStr(buf);
  735.       return(FALSE);
  736.    }
  737.    strcpy(old_user, _user);
  738.  
  739.    strcpy(_user, new_user);
  740.  
  741.    // send message to all servers, printing out the change you've made
  742.    if(!sendMsg(USER, new_user, _group, old_user)) {
  743.       _recvConfText->putStr("ERROR> sendMsg() failed.");
  744.       return(FALSE);
  745.    }
  746.  
  747.    return(TRUE);
  748. }
  749.  
  750.  
  751. Boolean ConfDoc::sendMsg(short flags, char *user, char *group, char *data)
  752. {
  753.    struct Message msg;
  754.  
  755.    if(numWords(data) == 0) {
  756.       if(!((flags == LOGIN) || (flags == LOGOUT) || (flags == RING)))
  757.          return(FALSE);
  758.    }
  759.  
  760.    msg.flags = flags;
  761.    strcpy(msg.user, user);
  762.    strcpy(msg.group, group);
  763.    if(msg.flags == USER) {
  764.       strcpy(msg.data, data);
  765.    }
  766.    else {
  767.       strcpy(msg.data, "(");
  768.       strcat(msg.data, _user);
  769.       strcat(msg.data, ".");
  770.       strcat(msg.data, _group);
  771.       strcat(msg.data, "): ");
  772.       strcat(msg.data, data);
  773.    }
  774.  
  775.    int len = sizeof(msg) - sizeof(msg.data) + strlen(msg.data);
  776.  
  777.    if(sendto(_socket, &msg, sizeof(msg), 0, &_sendAddr, _addrSize) < 0) {
  778.       perror("send");
  779.       return(FALSE);
  780.    }
  781.    bzero(msg.data, sizeof(msg.data));  // clear send buffer
  782.    return(TRUE);
  783. }
  784.  
  785.  
  786. Boolean ConfDoc::sendBroadcast(char *buf)
  787. {
  788.    char *word;
  789.    char data[256];
  790.    int n = 2;
  791.  
  792.    if(numWords(buf) == 1) {
  793.       _recvConfText->putStr("ERROR> No message supplied.");
  794.       return(FALSE);
  795.    }
  796.  
  797.    strcpy(data, "");  // initialize local buffer
  798.    while((word = getWord(buf, n++)) != NULL) {
  799.       strcat(data, word);
  800.       strcat(data, " ");
  801.    }
  802.  
  803.    if(!sendMsg(BROADCAST, _user, _group, data)) {
  804.       _recvConfText->putStr("ERROR> sendMsg() failed.");
  805.       return(FALSE);
  806.    }
  807.    return(TRUE);
  808. }
  809.  
  810.  
  811. Boolean ConfDoc::sendPrivate(char *buf, int ring)
  812. {
  813.    int n = 3;
  814.    char user[30];
  815.    char data[256];
  816.    char *word;
  817.  
  818.    if(ring) {
  819.       if(numWords(buf) == 1) {
  820.          _recvConfText->putStr("ERROR> No user supplied.");
  821.          return(FALSE);
  822.       }
  823.    }
  824.    else {
  825.       if(numWords(buf) <= 2) {
  826.          _recvConfText->putStr("ERROR> No user or message supplied.");
  827.          return(FALSE);
  828.       }
  829.    }
  830.  
  831.    strcpy(user, getWord(buf, 2));
  832.  
  833.    // check if user is logged in
  834.    if(!_userList.find(user)) {
  835.       char buf[MAX_BUF_SIZE];
  836.       strcpy(buf, "ERROR> user '");
  837.       strcat(buf, user);
  838.       strcat(buf, "' not logged in.");
  839.       _recvConfText->putStr(buf);
  840.       return(FALSE);
  841.    }
  842.  
  843.    strcpy(data, "");  // initialize local buffer
  844.    while((word = getWord(buf, n++)) != NULL) {
  845.       strcat(data, word);
  846.       strcat(data, " ");
  847.    }
  848.  
  849.    // echo the text locally (to keep track of what you've typed
  850.    char tdata[256];
  851.    strcpy(tdata, "TO> (");
  852.    strcat(tdata, user);
  853.    strcat(tdata, "): ");
  854.    strcat(tdata, data);
  855.    _recvConfText->putStr(tdata);
  856.  
  857.    // this is a special case where we use the user field to represent
  858.    // the recipient, rather than the sender
  859.    if(ring) {
  860.       if(!sendMsg(RING, user, _group, data)) {
  861.          _recvConfText->putStr("ERROR> sendMsg() failed.");
  862.          return(FALSE);
  863.       }
  864.    }
  865.    else {
  866.       if(!sendMsg(PRIVATE, user, _group, data)) {
  867.          _recvConfText->putStr("ERROR> sendMsg() failed.");
  868.          return(FALSE);
  869.       }
  870.    }
  871.    return(TRUE);
  872. }
  873.  
  874.  
  875. Boolean ConfDoc::sendUrgent(char *buf)
  876. {
  877.    char *word;
  878.    char data[256];
  879.    int n = 2;
  880.  
  881.    if(numWords(buf) == 1) {
  882.       _recvConfText->putStr("ERROR> No message supplied.");
  883.       return(FALSE);
  884.    }
  885.  
  886.    bzero(data, sizeof(data));  // initialize local buffer
  887.    while((word = getWord(buf, n++)) != NULL) {
  888.       strcat(data, word);
  889.       strcat(data, " ");
  890.    }
  891.  
  892.    if(!sendMsg(URGENT, _user, _group, data)) {
  893.       _recvConfText->putStr("ERROR> sendMsg() failed.");
  894.       return(FALSE);
  895.    }
  896.    return(TRUE);
  897. }
  898.  
  899.  
  900. void ConfDoc::setInputLabel(char* label)
  901. {
  902.    char labelText[64];
  903.    strcpy(labelText, "Input: (group=");
  904.    strcat(labelText, label);
  905.    strcat(labelText, ")");
  906.    XmString labelString = XmStringCreate(labelText, XmSTRING_DEFAULT_CHARSET);
  907.    Arg args[1];
  908.    XtSetArg(args[0], XmNlabelString, labelString);
  909.    XtSetValues(_inputLabel, args, 1);
  910. }
  911.  
  912.  
  913. int ConfDoc::numWords(char *buf)
  914. {
  915.    int count = 0;
  916.    int end = 0;
  917.  
  918.    while(!end) {
  919.       while(*buf == ' ') buf++;
  920.       if(*buf == '\0') return(count);
  921.       count++;
  922.       while(*buf != ' ') {
  923.          if(*buf == '\0') return(count);
  924.          buf++;
  925.       }
  926.    }
  927. }
  928.  
  929.  
  930. char *ConfDoc::getWord(char *buf, int pos)
  931. {
  932.    static char word[256];
  933.    int n = 0;
  934.    int curpos = 0;
  935.  
  936.    if(numWords(buf) < pos) return(NULL);
  937.  
  938.    while(pos != curpos) {
  939.       while(*buf == ' ') buf++;
  940.       curpos++;
  941.  
  942.       if(*buf == '\0') return(NULL);
  943.  
  944.       while(*buf != ' ') {
  945.          if(*buf == '\0') {
  946.             curpos = pos;
  947.             break;
  948.          }
  949.          word[n++] = *buf++;
  950.       }
  951.       word[n] = '\0';
  952.       n = 0;
  953.    }
  954.    return(word);
  955. }
  956.  
  957.